/*
 *  $Id: Newsreader.java 633 2012-05-01 18:59:55Z k42b3.x@googlemail.com $
 *
 * This is an Amun demo application. The demo application showcases and explains
 * functionalities of Amun. The code is released under the GPLv3. More
 * informations about Amun at http://amun.phpsx.org.
 *
 * This is a newsreader app wich loads news from an remote amun instance defined
 * in the BASE_URL. It is also possible to add a comment for each news. It
 * showcases the basic functionalities of an app to authenticate, receive and
 * post data to Amun. If no CONFIG_FILE was written the app tries to establish
 * OAuth credentials to act on the users behalf. The credentials are written to
 * the CONFIG_FILE. Simply delete the CONFIG_FILE if you want to restart the
 * authentication process. By default the consumer key and consumer secret by
 * the demo application on amun.phpsx.org are used but it can be used by any
 * other amun instance with the news service installed.
 */

package org.phpsx.amun.newsreader;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;
import java.util.TimeZone;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableColumn;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.k42b3.neodym.Http;
import com.k42b3.neodym.ServiceItem;
import com.k42b3.neodym.Services;
import com.k42b3.neodym.oauth.Oauth;
import com.k42b3.neodym.oauth.OauthProvider;

/**
 * Newsreader
 *
 * @author     Christoph Kappestein <k42b3.x@gmail.com>
 * @license    http://www.gnu.org/licenses/gpl.html GPLv3
 * @link       http://amun.phpsx.org
 * @category   example
 * @package    newsreader
 * @version    $Revision: 633 $
 */
public class Newsreader extends JFrame
{
	public final static String VERSION = "0.0.3";

	public final static String CONFIG_FILE = ".newsreader.config";
	public final static String BASE_URL = "http://amun.phpsx.org";
	public final static String CONSUMER_KEY = "022170e918332b13167e33b74f4588e19c1bae5d";
	public final static String CONSUMER_SECRET = "82bab8310d0b3a70f1406a0e46f0690799d3b8fa";

	public static Http http;
	public static Services availableServices;
	public static User user;
	public static NewsModel news;
	public static CommentsModel comments;

	private String newsUrl;
	private String commentsUrl;
	private NewsTable tableNews;
	private CommentsTable tableComments;

	private JTextArea txtComment;
	private JButton btnSubmit;

	public Newsreader()
	{
		this.setTitle("Newsreader (version: " + Newsreader.VERSION + ")");
		this.setLocation(100, 100);
		this.setSize(450, 650);
		this.setMinimumSize(this.getSize());
		this.setLayout(new BorderLayout());
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		try
		{
			// do authentication
			this.doAuthentication();

			// build gui
			JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, this.buildNews(), this.buildComments());
			splitPane.setDividerLocation(300);

			this.add(splitPane, BorderLayout.CENTER);
			this.add(this.buildForm(), BorderLayout.SOUTH);
		}
		catch(Exception e)
		{
			JOptionPane.showMessageDialog(null, e.getMessage());
		}

		this.pack();
		this.setVisible(true);

		// load news
		NewsModel.load(newsUrl);
	}

	private Component buildNews()
	{
		// build news table
		tableNews = new NewsTable(getNews(), new NewsColumnModel());
		tableNews.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		tableNews.setTableHeader(null);
		tableNews.addMouseListener(new MouseListener() {

			public void mouseReleased(MouseEvent e)
			{
			}

			public void mousePressed(MouseEvent e)
			{
			}

			public void mouseExited(MouseEvent e)
			{
			}

			public void mouseEntered(MouseEvent e)
			{
			}

			public void mouseClicked(MouseEvent e)
			{
				if(e.getClickCount() >= 1)
				{
					int row = tableNews.getSelectedRow();

					if(row >= 0)
					{
						int id = Integer.parseInt(getNews().getValueAt(row, 0).toString());

						CommentsModel.load(commentsUrl + "?fields=id,globalId,userId,text,date,authorName,authorThumbnailUrl&filterBy=refId&filterOp=equals&filterValue=" + id);

						btnSubmit.setEnabled(true);
					}
				}
			}

		});

		JScrollPane scpNews = new JScrollPane(tableNews);
		scpNews.setPreferredSize(new Dimension(450, 200));

		return scpNews;
	}

	private Component buildComments()
	{
		// build comments table
		tableComments = new CommentsTable(getComments(), new CommentsColumnModel());
		tableComments.setTableHeader(null);

		JScrollPane scpComments = new JScrollPane(tableComments);
		scpComments.setPreferredSize(new Dimension(450, 200));

		return scpComments;
	}

	private Component buildForm()
	{
		// build comments form
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());

		txtComment = new JTextArea();

		JScrollPane scpText = new JScrollPane(txtComment);
		scpText.setPreferredSize(new Dimension(450, 75));

		panel.add(scpText, BorderLayout.CENTER);

		// button panels
		JPanel panelButtons = new JPanel();
		panelButtons.setLayout(new FlowLayout(FlowLayout.LEFT));

		btnSubmit = new JButton("Submit");
		btnSubmit.setEnabled(false);
		btnSubmit.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e)
			{
				if(btnSubmit.isEnabled())
				{
					try
					{
						int row = tableNews.getSelectedRow();

						if(row == -1)
						{
							throw new Exception("No News selected");
						}

						// headers
						HashMap<String, String> headers = new HashMap<String, String>();
						headers.put("Content-Type", "application/xml");
						headers.put("Accept", "application/xml");

						// body
						int pageId = Integer.parseInt(getNews().getValueAt(row, 2).toString());
						int refId = Integer.parseInt(getNews().getValueAt(row, 0).toString());

						String body = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
						body+= "<comment>\n";
						body+= "	<pageId>" + pageId + "</pageId>\n";
						body+= "	<refId>" + refId + "</refId>\n";
						body+= "	<text><![CDATA[" + txtComment.getText() + "]]></text>\n";
						body+= "</comment>";

						Document response = getHttp().requestXml(Http.POST, commentsUrl, headers, body);

						Node success = response.getElementsByTagName("success").item(0);
						Node text = response.getElementsByTagName("text").item(0);

						if(success != null && text != null)
						{
							String msg = text.getTextContent();

							if(success.getTextContent().equals("true"))
							{
								JOptionPane.showMessageDialog(null, msg);

								txtComment.setText("");

								CommentsModel.load(commentsUrl + "?fields=id,globalId,userId,text,date,authorName,authorThumbnailUrl&filterBy=refId&filterOp=equals&filterValue=" + refId);
							}
							else
							{
								throw new Exception(msg);
							}
						}
						else
						{
							throw new Exception("Invalid response");
						}
					}
					catch(Exception ex)
					{
						JOptionPane.showMessageDialog(null, ex.getMessage());
					}
				}
			}

		});

		panelButtons.add(btnSubmit);

		panel.add(panelButtons, BorderLayout.SOUTH);

		return panel;
	}

	/**
	 * Discovers the OAuth endpoints and
	 *
	 * @throws Exception
	 */
	private void doAuthentication() throws Exception
	{
		// get oauth services
		String requestUrl = getAvailableServices().getItem("http://oauth.net/core/1.0/endpoint/request").getUri();
		String authorizationUrl = getAvailableServices().getItem("http://oauth.net/core/1.0/endpoint/authorize").getUri();
		String accessUrl = getAvailableServices().getItem("http://oauth.net/core/1.0/endpoint/access").getUri();

		OauthProvider provider = new OauthProvider(requestUrl, authorizationUrl, accessUrl, CONSUMER_KEY, CONSUMER_SECRET);
		Oauth oauth = new Oauth(getHttp(), provider);

		// load configuration
		File config = new File(Newsreader.CONFIG_FILE);

		if(config.isFile())
		{
			Properties prop = new Properties();
			prop.loadFromXML(new FileInputStream(config));

			if(prop.containsKey("token") && prop.containsKey("token_secret"))
			{
				oauth.auth(prop.getProperty("token"), prop.getProperty("token_secret"));
			}
			else
			{
				throw new Exception("No token or token_secret set in config");
			}
		}
		else
		{
			throw new Exception("No configuration set");
		}

		// get news services
		ServiceItem item = getAvailableServices().getItem("http://ns.amun-project.org/2011/amun/service/news");

		if(item != null)
		{
			newsUrl = item.getUri();
		}
		else
		{
			throw new Exception("Could not found news service");
		}

		// get comments services
		item = getAvailableServices().getItem("http://ns.amun-project.org/2011/amun/service/comment");

		if(item != null)
		{
			commentsUrl = item.getUri();
		}
		else
		{
			throw new Exception("Could not found comment service");
		}

		// add oauth to http library to send signed requests
		getHttp().setOauth(oauth);
	}

	public static Http getHttp()
	{
		if(http == null)
		{
			http = new Http();
		}

		return http;
	}

	/**
	 * Discovers all available services from the XRDS
	 *
	 * @throws Exception
	 * @return Services
	 */
	public static Services getAvailableServices() throws Exception
	{
		if(availableServices == null)
		{
			availableServices = new Services(getHttp(), BASE_URL);
			availableServices.discover();
		}

		return availableServices;
	}

	public static User getUser() throws Exception
	{
		if(user == null)
		{
			ServiceItem item = getAvailableServices().getItem("http://ns.amun-project.org/2011/amun/service/my/verifyCredentials");

			if(item != null)
			{
				Document cred = getHttp().requestXml(Http.GET, item.getUri());

				int id = Integer.parseInt(cred.getElementsByTagName("id").item(0).getTextContent());
				String name = cred.getElementsByTagName("name").item(0).getTextContent();
				String profileUrl = cred.getElementsByTagName("profileUrl").item(0).getTextContent();
				String thumbnailUrl = cred.getElementsByTagName("thumbnailUrl").item(0).getTextContent();
				String timezone = cred.getElementsByTagName("timezone").item(0).getTextContent();

				TimeZone.setDefault(TimeZone.getTimeZone(timezone));

				user = new User(id, name, profileUrl, thumbnailUrl, timezone);
			}
			else
			{
				throw new Exception("Could not get user informations");
			}
		}

		return user;
	}

	public static NewsModel getNews()
	{
		if(news == null)
		{
			news = new NewsModel();
		}

		return news;
	}

	public static CommentsModel getComments()
	{
		if(comments == null)
		{
			comments = new CommentsModel();
		}

		return comments;
	}

	class NewsColumnModel extends DefaultTableColumnModel
	{
		public NewsColumnModel()
		{
			this.addColumn(new TableColumn());
		}
	}

	class CommentsColumnModel extends DefaultTableColumnModel
	{
		public CommentsColumnModel()
		{
			this.addColumn(new TableColumn());
		}
	}
}
